package pool

import (
	"github.com/618lf/swakx-go/pkg/future"
	"github.com/618lf/swakx-go/pkg/gofunc"
	"github.com/618lf/swakx-go/pkg/tools/panics"
	"github.com/panjf2000/ants/v2"
	"time"
)

const (
	// DefaultAntsPoolSize sets up the capacity of worker pool, 256 * 1024.
	DefaultAntsPoolSize = 1 << 18

	// ExpiryDuration is the interval time to clean up those expired workers.
	ExpiryDuration = 10 * time.Second

	// Nonblocking decides what to do when submitting a new task to a full worker pool: waiting for a available worker
	// or returning nil directly.
	Nonblocking = false
)

func init() {
	// It releases the default pool from ants.
	ants.Release()
}

// WorkerPool is the alias of ants.Pool.
type WorkerPool interface {
	Go(func()) error
	Execute(runnable gofunc.Runnable) error
	Submit(runnable gofunc.Runnable) future.Future
}

type workerPool struct {
	WorkerPool
	pool         *ants.Pool
	panicHandler func(interface{})
}

func (wp *workerPool) Go(f func()) error {
	return wp.pool.Submit(f)
}

func (wp *workerPool) Execute(runnable gofunc.Runnable) error {
	return wp.Go(func() {
		defer panics.CatchPanicWithHandleError(func(err error) {
			if wp.panicHandler != nil {
				wp.panicHandler(err)
			}
		})
		runnable()
	})
}

func (wp *workerPool) Submit(runnable gofunc.Runnable) future.Future {
	f := future.NewFuture()
	err := wp.Go(func() {
		defer panics.CatchPanicWithHandleError(func(err error) {
			f.CompleteExceptionally(err)
			if wp.panicHandler != nil {
				wp.panicHandler(err)
			}
		})

		// run
		runnable()

		// complete future
		f.Complete(nil)
	})

	// add task error
	if err != nil {
		f.CompleteExceptionally(err)
	}

	// return future
	return f
}

func (wp *workerPool) Promise(supplier gofunc.Supplier) future.Promise {
	p := future.NewPromise(func(resolve future.Resolve, reject future.Reject) {
		err := wp.Go(func() {
			defer panics.CatchPanicWithHandleError(func(err error) {
				reject(err)
				if wp.panicHandler != nil {
					wp.panicHandler(err)
				}
			})
			res := supplier()
			resolve(res)
		})

		// add task error
		if err != nil {
			reject(err)
		}
	})

	return p
}

// NewWorkerPool instantiates a non-blocking *WorkerPool with the capacity of DefaultAntsPoolSize.
func NewWorkerPool(size int) WorkerPool {
	if size <= 0 {
		size = DefaultAntsPoolSize
	}
	options := ants.Options{ExpiryDuration: ExpiryDuration, Nonblocking: Nonblocking}
	defaultAntsPool, _ := ants.NewPool(size, ants.WithOptions(options))
	return &workerPool{
		pool: defaultAntsPool,
	}
}
